home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / DJGPP / DJSRC111.ZIP / go32 / ed / symsaout.c < prev    next >
C/C++ Source or Header  |  1993-06-28  |  16KB  |  706 lines

  1. /* This is file SYMS.C */
  2. /*
  3. ** Copyright (C) 1993 DJ Delorie, 24 Kirsten Ave, Rochester NH 03867-2954
  4. **
  5. ** This file is distributed under the terms listed in the document
  6. ** "copying.dj", available from DJ Delorie at the address above.
  7. ** A copy of "copying.dj" should accompany this file; if not, a copy
  8. ** should be available from where this file was obtained.  This file
  9. ** may not be distributed without a verbatim copy of "copying.dj".
  10. **
  11. ** This file is distributed WITHOUT ANY WARRANTY; without even the implied
  12. ** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. */
  14.  
  15. #include <stdio.h>
  16. #include <fcntl.h>
  17. #include <string.h>
  18. #include <stdlib.h>
  19. #include <ctype.h>
  20. #include <dos.h>
  21. #include <io.h>
  22.  
  23. #include "ed.h"
  24.  
  25. #define DO_SYMS 0
  26.  
  27. int undefined_symbol=0;
  28.  
  29. #define Ofs(n) ((int)&(((TSS *)0)->n))
  30.  
  31. struct {
  32.   char *name;
  33.   int size;
  34.   int ofs;
  35.   } regs[] = {
  36.   "%eip", 4, Ofs(tss_eip),
  37.   "%eflags", 4, Ofs(tss_eflags),
  38.   "%eax", 4, Ofs(tss_eax),
  39.   "%ebx", 4, Ofs(tss_ebx),
  40.   "%ecx", 4, Ofs(tss_ecx),
  41.   "%edx", 4, Ofs(tss_edx),
  42.   "%esp", 4, Ofs(tss_esp),
  43.   "%ebp", 4, Ofs(tss_ebp),
  44.   "%esi", 4, Ofs(tss_esi),
  45.   "%edi", 4, Ofs(tss_edi),
  46.   "%ax", 2, Ofs(tss_eax),
  47.   "%bx", 2, Ofs(tss_ebx),
  48.   "%cx", 2, Ofs(tss_ecx),
  49.   "%dx", 2, Ofs(tss_edx),
  50.   "%ah", 1, Ofs(tss_eax)+1,
  51.   "%bh", 1, Ofs(tss_ebx)+1,
  52.   "%ch", 1, Ofs(tss_ecx)+1,
  53.   "%dh", 1, Ofs(tss_edx)+1,
  54.   "%al", 1, Ofs(tss_eax),
  55.   "%bl", 1, Ofs(tss_ebx),
  56.   "%cl", 1, Ofs(tss_ecx),
  57.   "%dl", 1, Ofs(tss_edx),
  58.   0, 0, 0
  59. };
  60.  
  61. #if !DO_SYMS
  62.  
  63. void syms_init(char *fname) {}
  64. void syms_list(int byval) {}
  65. void syms_listwild(char *pattern) {}
  66.  
  67. word32 syms_name2val(char *name)
  68. {
  69.   if (isdigit(name[0]))
  70.   {
  71.     int v;
  72.     if (strncmp(name, "0x", 2) == 0)
  73.       sscanf(name+2, "%x", &v);
  74.     else
  75.       sscanf(name, "%d", &v);
  76.     undefined_symbol = 0;
  77.     return v;
  78.   }
  79.   else
  80.   {
  81.     undefined_symbol = 1;
  82.     return 0;
  83.   }
  84. }
  85.  
  86. char *syms_val2name(word32 val, word32 *delta)
  87. {
  88.   static char noname_buf[20];
  89.   sprintf(noname_buf, "%#lx", val);
  90.   *delta = 0;
  91.   return noname_buf;
  92. }
  93.  
  94. char *syms_val2line(word32 val, int *lineret, int exact)
  95. {
  96.   return 0;
  97. }
  98.  
  99. #else
  100.  
  101. #define SYMADDR        0xa0000000
  102. #define SYMSPACE    ((sizeof(TSS) + 1023) & ~1023)   /* collect this many symbols in real mode */
  103.  
  104. static word32 sym_brk=SYMADDR;
  105. static word32 sym_page=SYMADDR;
  106. static char  *sym_ptr;
  107. static char  *sym_buffer;
  108. static int    sym_left;
  109.  
  110.  
  111. word32 salloc(word32 size)
  112. {
  113.   word32 r = sym_brk;
  114.   size = (size + 3) & ~3;
  115.   sym_brk += size;
  116.   return r;
  117. }
  118.  
  119. void symwrite(void *ptr,int size)
  120. {
  121.   size = (size + 3) & ~3;
  122.   if(size <= 0) return;
  123.   while(sym_left < size) {
  124.     if(sym_left != 0) {
  125.       memcpy(sym_ptr,ptr,sym_left);
  126.       size -= sym_left;
  127.       ptr = (void *)((char *)ptr + sym_left);
  128.     }
  129.     memput(sym_page,sym_buffer,SYMSPACE);
  130.     sym_page += SYMSPACE;
  131.     sym_left = SYMSPACE;
  132.     sym_ptr = sym_buffer;
  133.   }
  134.   if(size > 0) {
  135.     memcpy(sym_ptr,ptr,size);
  136.     sym_ptr += size;
  137.     sym_left -= size;
  138.   }
  139. }
  140.  
  141. void symwrite_at(word32 addr,void *ptr,int size)
  142. {
  143.   size = (size + 3) & ~3;
  144.   if(size <= 0) return;
  145.   if(addr < sym_page) {
  146.     word32 written_size = sym_page - addr;
  147.     if(written_size < size) {
  148.       memput(addr,ptr,(word16)written_size);
  149.       addr += written_size;
  150.       size -= (int)written_size;
  151.       ptr = (void *)((char *)ptr + (int)written_size);
  152.     }
  153.     else {
  154.       memput(addr,ptr,size);
  155.       size = 0;
  156.     }
  157.   }
  158.   if(size > 0) {
  159.     int offset = (int)(addr - sym_page);
  160.     memcpy(&sym_buffer[offset],ptr,size);
  161.   }
  162. }
  163.  
  164. #define symsput(where,ptr,size)        memput(where,ptr,size)
  165. #define symsget(where,ptr,size)        memget(where,ptr,size)
  166.  
  167. typedef struct SYMTREE {
  168.   word32 me;
  169.   word32 nleft, nright, ndown;
  170.   word32 vleft, vright, vdown;
  171.   word32 val;
  172.   word32 type;
  173.   word32 name_len;
  174. } SYMTREE;
  175.  
  176. typedef struct FILENODE {
  177.   word32 me;
  178.   word32 next;
  179.   word32 line_list;
  180.   word32 first_addr, last_addr;
  181.   word32 name_len;
  182. } FILENODE;
  183.  
  184. typedef struct LINENODE {
  185.   word32 me;
  186.   word32 next;
  187.   word32 num;
  188.   word32 addr;
  189. } LINENODE;
  190.  
  191. static word32 symtree_root=0;
  192. static word32 file_list=0;
  193.  
  194. static char tmps[256];
  195. static char tmps2[256];
  196.  
  197. static symtree_print(word32 s, int byval, int level)
  198. {
  199.   SYMTREE tmp;
  200.   if (s == 0)
  201.     return;
  202.   symsget(s, &tmp, sizeof(tmp));
  203.   if (byval)
  204.     symtree_print(tmp.vleft, byval, level+1);
  205.   else
  206.     symtree_print(tmp.nleft, byval, level+1);
  207.  
  208.   symsget(tmp.me+sizeof(tmp), tmps, tmp.name_len);
  209.   printf("0x%08lx 0x%08lx %*s%s\n", tmp.val, tmp.type, level, "", tmps);
  210.  
  211.   if (byval)
  212.     symtree_print(tmp.vdown, byval, level);
  213.   else
  214.     symtree_print(tmp.ndown, byval, level);
  215.  
  216.   if (byval)
  217.     symtree_print(tmp.vright, byval, level+1);
  218.   else
  219.     symtree_print(tmp.nright, byval, level+1);
  220. }
  221.  
  222. void syms_list(v)
  223. {
  224.   symtree_print(symtree_root, v, 0);
  225. }
  226.  
  227. typedef struct SYM_ENTRY {
  228.   word32 string_off;
  229.   word8 type;
  230.   word8 other;
  231.   word16 desc;
  232.   word32 val;
  233. } SYM_ENTRY;
  234.  
  235. void syms_init(char *fname)
  236. {
  237.   GNU_AOUT header;
  238.   int j, per, oldper=-1;
  239.   word32 nsyms, i;
  240.   FILE *fd,*sfd;
  241.   word32 sfd_base,sfd_pos;
  242.   SYM_ENTRY sym;
  243.   char *strings, *cp;
  244.   word32 string_size;
  245.   word32 last_dot_o=0;
  246.   FILENODE filen;
  247.   LINENODE linen;
  248.   SYMTREE  temp;
  249.   char symbuf[SYMSPACE];
  250.  
  251.   sym_ptr = sym_buffer = symbuf;
  252.   sym_left = SYMSPACE;
  253.  
  254.   filen.me = 0;
  255.   linen.me = 0;
  256.  
  257.   fd = fopen(fname, "rb");
  258.   sfd = fopen(fname, "rb");
  259.  
  260.   fread(&header, sizeof(header), 1, fd);
  261.   switch((word16)header.info) {
  262.     case 0x014c:
  263.       fseek(fd, 0xa8L, 0);
  264.       fread(&header, sizeof(header), 1, fd);
  265.       fseek(fd, 0xa8+sizeof(header) + header.tsize + header.dsize, 0);
  266.       break;
  267.     case 0x010b:
  268.     case 0x0107:
  269.       fseek(fd, sizeof(header)+header.tsize+header.dsize+header.txrel+header.dtrel, 0);
  270.       break;
  271.     default:
  272.       fclose(fd);
  273.       fclose(sfd);
  274.       return;
  275.   }
  276.  
  277.   printf("Reading symbols...   0%%");
  278.   fflush(stdout);
  279.  
  280.   /* reserve space for the symbol tree root pointer */
  281.   salloc(sizeof(word32));
  282.   symwrite(&symtree_root,sizeof(word32));
  283.  
  284.   nsyms = header.symsize / sizeof(SYM_ENTRY);
  285.  
  286.   fseek(sfd, ftell(fd)+header.symsize, 0);
  287.   sfd_pos = sfd_base = ftell(sfd);
  288.  
  289.   for (i=0; i<nsyms; i++)
  290.   {
  291.     if (nsyms > 1)
  292.       per = i*100L/(nsyms-1);
  293.     else
  294.       per = 100;
  295.     if (per != oldper)
  296.     {
  297.       printf("\b\b\b\b%3d%%", per);
  298.       fflush(stdout);
  299.       oldper = per;
  300.     }
  301.     fread(&sym, sizeof(sym),1, fd);
  302.     if (sym.string_off) {
  303.       word32 where = sfd_base + sym.string_off;
  304.       if(where != sfd_pos) fseek(sfd,(sfd_pos = where),0);
  305.       for(cp = tmps2; (*cp = getc(sfd)) != '\0'; cp++);
  306.       sfd_pos += (int)(cp - tmps2) + 1;
  307.     }
  308.     switch (sym.type)
  309.     {
  310.       case N_TEXT:
  311.       case N_TEXT | N_EXT:
  312.     cp = tmps2;
  313.     cp += strlen(cp) - 2;
  314.     if (strcmp(cp, ".o") == 0)
  315.     {
  316.       last_dot_o = sym.val;
  317.       if (filen.me && (filen.last_addr == 0))
  318.       {
  319.         filen.last_addr = last_dot_o - 1;
  320.       }
  321.       break;
  322.     }
  323.     if (strncmp(tmps2, "___gnu", 6) == 0)
  324.       break;
  325.     if (strcmp(cp, "d.") == 0) /* as in gcc_compiled. */
  326.       break;
  327.       case N_DATA:
  328.       case N_DATA | N_EXT:
  329.       case N_ABS:
  330.       case N_ABS | N_EXT:
  331.       case N_BSS:
  332.       case N_BSS | N_EXT:
  333.       case N_FN:
  334.       case N_SETV:
  335.       case N_SETV | N_EXT:
  336.       case N_SETA:
  337.       case N_SETA | N_EXT:
  338.       case N_SETT:
  339.       case N_SETT | N_EXT:
  340.       case N_SETD:
  341.       case N_SETD | N_EXT:
  342.       case N_SETB:
  343.       case N_SETB | N_EXT:
  344.       case N_INDR:
  345.       case N_INDR | N_EXT:
  346.     if (sym.string_off) {
  347.       memset(&temp, 0, sizeof(SYMTREE));
  348.       temp.name_len = strlen(tmps2) + 1;
  349.       temp.me = salloc(sizeof(temp)+temp.name_len);
  350.       temp.val = sym.val;
  351.       temp.type = sym.type;
  352.       temp.nleft = symtree_root;
  353.       symwrite(&temp, sizeof(temp));
  354.       symwrite(tmps2, temp.name_len);
  355.       symtree_root = temp.me;
  356.     }
  357.     break;
  358.       case N_SO:
  359.     if(filen.me != 0) {
  360.       /* update previous file name symbol */
  361.       symwrite_at(filen.me,&filen, sizeof(filen));
  362.     }
  363.     memset(&filen, 0, sizeof(FILENODE));
  364.     filen.name_len = strlen(tmps2)+1;
  365.     filen.me = salloc(sizeof(FILENODE)+filen.name_len);
  366.     filen.next = file_list;
  367.     file_list = filen.me;
  368.     filen.first_addr = last_dot_o;
  369.     symwrite(&filen, sizeof(filen));
  370.     symwrite(tmps2, filen.name_len);
  371.     break;
  372.       case N_SLINE:
  373.     memset(&linen, 0, sizeof(linen));
  374.     linen.me = salloc(sizeof(LINENODE));
  375.     linen.next = filen.line_list;
  376.     filen.line_list = linen.me;
  377.     linen.num = sym.desc;
  378.     linen.addr = sym.val;
  379.     symwrite(&linen, sizeof(linen));
  380.     break;
  381.     }
  382.   }
  383.   fclose(fd);
  384.   fclose(sfd);
  385.   printf(" %ld symbol%s read\n", nsyms, nsyms==1 ? "" : "s");
  386.   if(filen.me != 0) {
  387.     /* update the last file name symbol */
  388.     symwrite_at(filen.me,&filen, sizeof(filen));
  389.   }
  390.   if(sym_left < SYMSPACE) {
  391.     /* flush symbol read buffer if necessary */
  392.     memput(sym_page,sym_buffer,(SYMSPACE - sym_left));
  393.   }
  394.   if(symtree_root) {
  395.     /*
  396.      * Construct symbol tree from the simple linked list built above.
  397.      * Do it using a 32-bit protected mode code fragment generated
  398.      * from the code in "sym32.c" using GCC
  399.      */
  400.     static char do_symsort32[] = {
  401. #     include "sym32.h"
  402.       0
  403.     };
  404.     TSS *old_tss = tss_ptr;
  405.  
  406.     printf("Building symbol trees... ");
  407.     fflush(stdout);
  408.     tss_ptr = &a_tss;
  409.     memput(SYMADDR,&symtree_root,sizeof(word32));   /* pass root to 32-bit code */
  410.     memcpy(symbuf,&a_tss,sizeof(TSS));            /* save arena TSS */
  411.     a_tss.tss_eip = 0xe0000000L +            /* set it up */
  412.     ((word32)(word16)FP_SEG(do_symsort32) << 4) +
  413.     (word32)(word16)FP_OFF(do_symsort32);
  414.     a_tss.tss_esp -= 0x20;
  415.     go_til_done();
  416.     memcpy(&a_tss,symbuf,sizeof(TSS));            /* restore ARENA TSS */
  417.     tss_ptr = old_tss;
  418.     printf("DONE\n");
  419.   }
  420. }
  421.  
  422. word32 syms_name2val(char *name)
  423. {
  424.   SYMTREE s;
  425.   int cval, idx, sign=1, i;
  426.   word32 v;
  427.   char *cp;
  428.  
  429.   undefined_symbol = 0;
  430.  
  431.   idx = 0;
  432.   sscanf(name, "%s", name);
  433.  
  434.   if (name[0] == 0)
  435.     return 0;
  436.  
  437.   if (name[0] == '-')
  438.   {
  439.     sign = -1;
  440.     name++;
  441.   }
  442.   else if (name[0] == '+')
  443.   {
  444.     name++;
  445.   }
  446.   if (isdigit(name[0]))
  447.   {
  448.     if (sign == -1)
  449.       return -strtol(name, 0, 0);
  450.     return strtol(name, 0, 0);
  451.   }
  452.  
  453.   cp = strpbrk(name, "+-");
  454.   if (cp)
  455.     idx = cp-name;
  456.   else
  457.     idx = strlen(name);
  458.  
  459.   if (name[0] == '%') /* register */
  460.   {
  461.     for (i=0; regs[i].name; i++)
  462.       if (strncmp(name, regs[i].name, idx) == 0)
  463.       {
  464.     switch (regs[i].size)
  465.     {
  466.       case 1:
  467.         v = *(word8 *)((word8 *)tss_ptr + regs[i].ofs);
  468.         break;
  469.       case 2:
  470.         v = *(word16 *)((word8 *)tss_ptr + regs[i].ofs);
  471.         break;
  472.       case 4:
  473.         v = *(word32 *)((word8 *)tss_ptr + regs[i].ofs);
  474.         break;
  475.     }
  476.     return v + syms_name2val(name+idx);
  477.       }
  478.   }
  479.  
  480.   for (i=0; i<idx; i++)
  481.     if (name[i] == '#')
  482.     {
  483.       FILENODE f;
  484.       LINENODE l;
  485.       int lnum;
  486.       sscanf(name+i+1, "%d", &lnum);
  487.       for (f.me=file_list; f.me; f.me=f.next)
  488.       {
  489.     symsget(f.me, &f, sizeof(f));
  490.     symsget(f.me+sizeof(f), tmps, f.name_len);
  491.     if ((strncmp(name, tmps, i) == 0) && (tmps[i] == 0))
  492.     {
  493.       for (l.me=f.line_list; l.me; l.me=l.next)
  494.       {
  495.         symsget(l.me, &l, sizeof(l));
  496.         if (l.num == lnum)
  497.           return l.addr + syms_name2val(name+idx);
  498.       }
  499.       printf("undefined line number %.*s\n", idx, name);
  500.       undefined_symbol = 1;
  501.       return 0;
  502.     }
  503.       }
  504.       printf("Undefined file name %.*s\n", i, name);
  505.       undefined_symbol = 1;
  506.       return 0;
  507.     }
  508.  
  509.   s.me = symtree_root;
  510.   while (s.me)
  511.   {
  512.     symsget(s.me, &s, sizeof(s));
  513.     symsget(s.me+sizeof(s), tmps, s.name_len);
  514.     cval = strncmp(name, tmps, idx);
  515.     if ((cval == 0) && tmps[idx])
  516.       cval = -1;
  517.     if (cval == 0)
  518.       return s.val*sign + syms_name2val(name+idx);
  519.     else if (cval < 0)
  520.       s.me = s.nleft;
  521.     else
  522.       s.me = s.nright;
  523.   }
  524.   s.me = symtree_root;
  525.   while (s.me)
  526.   {
  527.     symsget(s.me, &s, sizeof(s));
  528.     symsget(s.me+sizeof(s), tmps, s.name_len);
  529.     if (tmps[0] == '_')
  530.       cval = strncmp(name, tmps+1, idx);
  531.     else
  532.       cval = '_' - tmps[0];
  533.     if ((cval == 0) && tmps[idx+1])
  534.       cval = -1;
  535.     if (cval == 0)
  536.       return s.val*sign + syms_name2val(name+idx);
  537.     else if (cval < 0)
  538.       s.me = s.nleft;
  539.     else
  540.       s.me = s.nright;
  541.   }
  542.   printf("Undefined symbol %.*s\n", idx, name);
  543.   undefined_symbol = 1;
  544.   return 0;
  545. }
  546.  
  547. static char noname_buf[11];
  548.  
  549. char *syms_val2name(word32 val, word32 *delta)
  550. {
  551.   SYMTREE s, lasts;
  552.  
  553.   if (delta)
  554.     *delta = 0;
  555.   lasts.me = 0;
  556.   s.me = symtree_root;
  557.   while (s.me)
  558.   {
  559.     symsget(s.me, &s, sizeof(s));
  560.     if (s.val <= val)
  561.       lasts.me = s.me;
  562.     if (val == s.val)
  563.     {
  564.       while (s.vdown)
  565.     symsget(s.vdown, &s, sizeof(s));
  566.       symsget(s.me+sizeof(s), tmps2, s.name_len);
  567.       return tmps2;
  568.     }
  569.     else if (val < s.val)
  570.       s.me = s.vleft;
  571.     else
  572.       s.me = s.vright;
  573.   }
  574.   if (lasts.me)
  575.   {
  576.     symsget(lasts.me, &lasts, sizeof(lasts));
  577.     while (lasts.vdown)
  578.       symsget(lasts.vdown, &lasts, sizeof(lasts));
  579.     symsget(lasts.me+sizeof(lasts), tmps2, lasts.name_len);
  580.     if (strcmp(tmps2, "_etext") == 0)
  581.       goto noname;
  582.     if (strcmp(tmps2, "_end") == 0)
  583.       goto noname;
  584.     if (delta)
  585.       *delta = val - lasts.val;
  586.     return tmps2;
  587.   }
  588. noname:
  589.   sprintf(noname_buf, "%#lx", val);
  590.   return noname_buf;
  591. }
  592.  
  593. char *syms_val2line(word32 val, int *lineret, int exact)
  594. {
  595.   FILENODE filen;
  596.   LINENODE linen, closest;
  597.   closest.me = 0;
  598.   for (filen.me = file_list; filen.me; filen.me=filen.next)
  599.   {
  600.     symsget(filen.me, &filen, sizeof(filen));
  601.     if ((val <= filen.last_addr) && (val >= filen.first_addr))
  602.     {
  603.       for (linen.me=filen.line_list; linen.me; linen.me = linen.next)
  604.       {
  605.     symsget(linen.me, &linen, sizeof(linen));
  606.     if (val == linen.addr)
  607.     {
  608.       *lineret = linen.num;
  609.       symsget(filen.me+sizeof(filen), tmps2, filen.name_len);
  610.       return tmps2;
  611.     }
  612.     if (val > linen.addr)
  613.     {
  614.       if (!closest.me)
  615.         closest.me = linen.me;
  616.       else if (closest.addr < linen.addr)
  617.         closest.me = linen.me;
  618.     }
  619.       }
  620.       if (closest.me && !exact)
  621.       {
  622.     symsget(closest.me, &closest, sizeof(closest));
  623.     *lineret = closest.num;
  624.     symsget(filen.me+sizeof(filen), tmps2, filen.name_len);
  625.     return tmps2;
  626.       }
  627.     }
  628.   }
  629.   return 0;
  630. }
  631.  
  632. static char type2char(type)
  633. {
  634.   switch (type)
  635.   {
  636.     case N_TEXT:
  637.       return 't';
  638.     case N_TEXT | N_EXT:
  639.       return 'T';
  640.     case N_DATA:
  641.       return 'd';
  642.     case N_DATA | N_EXT:
  643.       return 'D';
  644.     case N_BSS:
  645.       return 'b';
  646.     case N_BSS | N_EXT:
  647.       return 'B';
  648.     default:
  649.       return ' ';
  650.   }
  651. }
  652.  
  653. static int linecnt, quit_list;
  654.  
  655. static syms_listwild2(word32 s_pos, char *pattern)
  656. {
  657.   SYMTREE s;
  658.   char *name;
  659.   int lnum;
  660.   s.me = s_pos;
  661.   if ((s.me == 0) || quit_list)
  662.     return;
  663.   symsget(s.me, &s, sizeof(s));
  664.   syms_listwild2(s.vleft, pattern);
  665.   if (quit_list)
  666.     return;
  667.   symsget(s.me+sizeof(s), tmps, s.name_len);
  668.   if (wild(pattern, tmps))
  669.   {
  670.     if (++linecnt > 20)
  671.     {
  672.       printf("--- More ---");
  673.       switch (getch())
  674.       {
  675.     case ' ':
  676.       linecnt = 0;
  677.       break;
  678.     case 13:
  679.       linecnt--;
  680.       break;
  681.     case 'q':
  682.       quit_list = 1;
  683.       return;
  684.       }
  685.       printf("\r            \r");
  686.     }
  687.     printf("0x%08lx %c %s", s.val, type2char(s.type), tmps);
  688.     name = syms_val2line(s.val, &lnum, 0);
  689.     if (name)
  690.       printf(", line %d of %s", lnum, name);
  691.     mputchar('\n');
  692.   }
  693.   syms_listwild2(s.vdown, pattern);
  694.   if (quit_list)
  695.     return;
  696.   syms_listwild2(s.vright, pattern);
  697. }
  698.  
  699. void syms_listwild(char *pattern)
  700. {
  701.   quit_list = linecnt = 0;
  702.   syms_listwild2(symtree_root, pattern);
  703. }
  704.  
  705. #endif
  706.